﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using QAToolsAddin.Components;
using QAToolsAddin.GenericDialogs.Fields;
using Excel = Microsoft.Office.Interop.Excel;

namespace QAToolsAddin.GenericDialogs {
    public delegate void ActionDelegate();

    public partial class InputBox : ExcelForm {
        private ActionDelegate _action;
        private bool _disableevents;
        private IField[] _fields;
        private string _formid;
        private Dictionary<string, object> defaultValues;

        private InputBox()
            : base(null) {
            InitializeComponent();
        }

        public InputBox(Excel.Application excelapp)
            : base(excelapp) {
            InitializeComponent();
        }

        public InputBox(Excel.Application excelapp, string title, params IField[] fields)
            : base(excelapp) {
            defaultValues = new Dictionary<string, object>();
            var stf = new StackTrace().GetFrame(1).GetMethod();
            _formid = stf.DeclaringType.Name + stf.Name;
            _fields = fields;
            RestoreFields();
            InitializeComponent();
            InitializeFields();
            Text = title;
        }

        private void RestoreFields() {
            for (int i = 0; i < _fields.Length; i++) {
                try {
                    var field = _fields[i];
                    if (field != null) {
                        defaultValues.Add(i.ToString(), field.Value);
                        object value;
                        if (field.Restore && Settings.TryGetParam(_formid, i.ToString(), out value))
                            field.Value = value;
                    }
                } catch { }
            }
        }

        private void SaveFields() {
            for (int i = 0; i < _fields.Length; i++) {
                var field = _fields[i];
                if (field != null && field.Restore)
                    Settings.SetParam(_formid, i.ToString(), field.Value);
            }
        }

        internal override void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
            e.Exception.Data.Add(e.Exception.Data.Count.ToString(), "Form: " + Text);
            foreach (Field argument in _fields) {
                if (argument != null)
                    e.Exception.Data.Add(e.Exception.Data.Count.ToString(), argument.ToDebugString());
            }
            Hide();
            base.Application_ThreadException(sender, e);
        }

        public void Show(ActionDelegate action) {
            _action = action;
            this.Show();
        }

        public DialogResult ShowDialog(ActionDelegate action) {
            _action = action;
            return ShowDialog();
        }

        private void InputBox_Load(object sender, EventArgs e) {
            if (panelInputs.Controls.Count != 0)
                panelInputs.Controls[0].Focus();
        }

        private void buttonOk_Click(object sender, EventArgs e) {
            if (ValidateFields()) {
                try {
                    if (_action != null)
                        _action();
                    DialogResult = DialogResult.OK;
                    SaveFields();
                    Close();
                } catch (InputError err) {
                    MsgBox.ShowError(base.ExcelApp, err.Message);
                    this.Focus();
                }
            }
        }

        private void control_Enter(object sender, EventArgs e) {
            if (_disableevents) return;
            var field = (Field)sender;
            SetDescriptionLabel(field.Control, field.Info, false);
        }

        private void control_KeyReturn(object sender, EventArgs e) {
            if (_disableevents) return;
            ValidateField((Field)sender);
        }

        private void SetDescriptionLabel(Control sender, string text, bool error) {
            lblError.SuspendLayout();
            _disableevents = true;
            lblError.Text = text;
            if (error) {
                lblError.ForeColor = Color.Red;
                sender.Focus();
                if (sender is TextBox)
                    ((TextBox)sender).SelectAll();
            } else
                lblError.ForeColor = Color.Black;
            lblError.ResumeLayout();
            _disableevents = false;
        }

        private void InitializeFields() {
            SuspendLayout();
            int yPos = 0;
            foreach (Field argument in _fields) {
                if (argument == null) continue;
                argument.Control.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
                argument.Control.Width = panelInputs.Width;
                argument.Control.Top = yPos;
                argument.Enter += control_Enter;
                argument.KeyReturn += control_KeyReturn;

                panelInputs.Controls.Add(argument.Control);
                if (string.IsNullOrEmpty(argument.Label) == false) {
                    panelLabels.Controls.Add(new Label {
                        Top = yPos,
                        Width = panelLabels.Width - 4,
                        Height = argument.Control.Height,
                        AutoSize = false,
                        TextAlign = ContentAlignment.MiddleRight,
                        Text = argument.Label
                    });
                }
                yPos += argument.Control.Height;
            }
            Height = (Height - ClientSize.Height) + lblError.Height + panelBottom.Height + yPos + 23;
            ResumeLayout();
        }

        private bool ValidateFields() {
            foreach (Field field in _fields) {
                if (field == null) continue;
                if (ValidateField(field) == false)
                    return false;
            }
            return true;
        }

        private bool ValidateField(Field field) {
            string errorMessage;
            if (field.Validate(out errorMessage) == false) {
                SetDescriptionLabel(field.Control, errorMessage, true);
                return false;
            }
            return true;
        }

        private void buttonCancel_Click(object sender, EventArgs e) {
            AutoValidate = AutoValidate.Disable;
            DialogResult = DialogResult.Cancel;
            Close();
        }

        private void btDefault_Click(object sender, EventArgs e) {
            for (int i = 0; i < _fields.Length; i++) {
                var field = _fields[i];
                if (field != null)
                    field.Value = defaultValues[i.ToString()];
            }
            _fields[0].Control.Select();
        }

    }

}
